#include "evdeploy/cv/detection/yoloworld_prepostprocessor.h"
#include "evdeploy/deploy.h"
#include "opencv2/core.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/imgproc.hpp"
#include <fstream>
#include <sstream>
#include <thread>
using namespace ev;

/**
 * 本测试是采用EVDeploy框架实现的一个yoloworld模型推理全过程的基本示例
 * 可以通过该示例学习EVDeploy的基本使用方法.
 * 本测试的运行方法:test_yolov5 [evdeploy_config] [test_image] [run_nums]
 * 输入参数为:1 配置文件 2 测试图片 3 连续推理次数
 * 例如:./bin/test_yoloworld ./tests/configs/algo_license_plate_det_trt.json ./tests/data/plate_lic.jpg 5
 */

void test_model(ev::Value config, std::string image_name, int run_nums)
{
    std::string uuid;
    uuid = config["uuid"].asString();
    ev::vision::YOLOWorldPreprocessor preprocessor;
    ev::vision::YOLOWorldPostprocessor postprocessor;

    for (int i = 0; i < run_nums; ++i)
    {
        //前处理,准备输入图像
        cv::Mat inMat = cv::imread(image_name);
        cv::Mat cv_in_mat1;
        preprocessor.Run(inMat, cv_in_mat1, 640);

        ev::EVModelData in;
        ev::EVModelData out;
        ev::EVMatData in_mat;

        in_mat.data = cv_in_mat1.data;
        in_mat.data_size = cv_in_mat1.cols * cv_in_mat1.rows * 3 * 4;
        in_mat.width = cv_in_mat1.cols;
        in_mat.height = cv_in_mat1.rows;
        in_mat.aligned_width = cv_in_mat1.cols;
        in_mat.aligned_height = cv_in_mat1.rows;
        in_mat.channel = 3;
        in_mat.loc = EV_DATA_HOST;
        in_mat.type = EV_UINT8;

        in.desc = NULL;
        in.mat = &in_mat;
        in.mat_num = 1; //输入图像数量,也可以是多张;如果是多张,则in.mat为数组指针

        // inference
        static std::mutex m_mutex; // 定义局部变量锁，被该类的所有对象共享，表示多对象时只能有一个对象在推理
        std::lock_guard<std::mutex> lock_guard(m_mutex); // 用于多线程时,线程安全
        EVDeploy::GetModel().RunInfer(uuid, &in, &out);
        EVLOG(INFO) << "RunInference done";
        //输出的数量由out.mat_num指示,输出的数据封装在out.mat中,如果是多个输出,则out.mat为指向多个输出的指针,
        //每一个输出的维度信息由out.mat[i]->dims指示
        //每一个输出的名称信息由out.mat[i]->desc指示
        for (int j = 0; j < out.mat_num; ++j)
        {
            EVMatData mat = out.mat[j];
            EVLOG(INFO) << "output name: " << mat.desc;
            for (int k = 0; k < mat.dims.size(); ++k)
            {
                EVLOG(INFO) << "dims " << k << ":" << mat.dims[k];
            }
        }

        //后处理
        std::vector<ev::vision::BoxInfo> objects;
        float scale = preprocessor.GetScale();

        postprocessor.Run(out.mat, objects, scale, 0.45, inMat.cols, inMat.rows);

        //可视化保存结果图
        EVLOG(INFO) << "objects.size() is " << objects.size();
        for (int i = 0; i < objects.size(); ++i)
        {
            int xmin = objects[i].x1;
            int ymin = objects[i].y1;
            int xmax = objects[i].x2;
            int ymax = objects[i].y2;

            cv::rectangle(inMat, cv::Point(xmin, ymin), cv::Point(xmax, ymax), cv::Scalar(0, 0, 255), 2);
            EVLOG(INFO) << "label, score, xmin, ymin, xmax, ymax:" << objects[i].label << " " << objects[i].score << " "
                        << xmin << " " << ymin << " " << xmax << " " << ymax;
        }
        cv::imwrite("yolo_world_result.jpg", inMat);

        //注意释放out.mat,否则会有内存泄露!!!!
        if (out.mat)
        {
            delete[] out.mat;
        }
    }
    //释放资源
    EVDeploy::GetModel().DestroyModel(uuid);
    EVLOG(INFO) << "processing done!!";
}

int main(int argc, char **argv)
{
    //根据配置文件初始化
    EVDeploy::GetModel().InitModel(argv[1]);

    //解析配置文件
    std::ifstream inf(argv[1]);
    ev::Value value;
    ev::Reader j_reader;
    j_reader.parse(inf, value);
    value = value["serving_models"];
    for (int i = 0; i < value.size(); ++i)
    {
        std::string uuid;
        uuid = value[i]["uuid"].asString();
        EVLOG(INFO) << "uuid is:==========" << uuid;
        //创建模型实例
        EVDeploy::GetModel().CreateModel(uuid);
        //模型推理过程
        test_model(value[i], std::string(argv[2]), atoi(argv[3]));
    }

    return 0;
}
